/*************************************************************
*
* cgm_tang_space.cpp
*  tangent space calculation
*************************************************************/
#include <m3d/m3d_stdafx.h>

#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#include <ap_comm.h>

#include <m3d.h>
#include <cgm.h>

#include "Objects.h"

//
// Local types
//
class cgmWED_TS : public cgmWED_SYS {
   cgmTANG_SPACE   *pTS;

   virtual int    MeshGetNFace        (void) { return pTS->GetNFace(); }
   virtual int    MeshGetNVert        (void) { return pTS->nWeldVert; }
   virtual int    MeshGetFaceNVert    (int faceNmb)  { return 3; };
   virtual int    MeshGetFaceVertInd  (int faceNmb, int vertNmb) { 
      return pTS->weldVertInd[pTS->GetFaceVertIdx(faceNmb, vertNmb)];
   }
public:
   cgmWED_TS (void) : pTS(NULL) {}
   
   void   SetTS (cgmTANG_SPACE *_pTS) { pTS = _pTS; }
};



//
// Static functions
//

//
// Static variables
//
/*************************************************************
* cgmComputeBinormal ()
*
*************************************************************/
void cgmComputeBinormal (m3dV *norm, m4dV *tang, m3dV *binorm)
{
   ASSERT(m3dIsEqual(tang->w, 1.f) || m3dIsEqual(tang->w, -1.f));

   m3dCrossProduct(norm, tang, binorm);
   _m3dScaleVector(binorm, tang->w);
   return;
}
/*************************************************************
* cgmComputeMatrWCS2TangSpace ()
*
*************************************************************/
void cgmComputeMatrWCS2TangSpace (m3dV *norm, m4dV *tang, m3dMATR *matr)
{
   m3dV binorm;

   cgmComputeBinormal(norm, tang, &binorm);

   matr->ElementRC(0, 0) = tang->x;   matr->ElementRC(0, 1) = binorm.x;   matr->ElementRC(0, 2) = norm->x;   matr->ElementRC(0, 3) = 0.f;
   matr->ElementRC(1, 0) = tang->y;   matr->ElementRC(1, 1) = binorm.y;   matr->ElementRC(1, 2) = norm->y;   matr->ElementRC(1, 3) = 0.f;
   matr->ElementRC(2, 0) = tang->z;   matr->ElementRC(2, 1) = binorm.z;   matr->ElementRC(2, 2) = norm->z;   matr->ElementRC(2, 3) = 0.f;
   matr->ElementRC(3, 0) = 0.f;       matr->ElementRC(3, 1) = 0.f;        matr->ElementRC(3, 2) = 0.f;       matr->ElementRC(3, 3) = 1.f;
   
   return;
}
/*************************************************************
* cgmComputeMatrTangSpace2WCS ()
*
*************************************************************/
void cgmComputeMatrTangSpace2WCS (m3dV *norm, m4dV *tang, m3dMATR *matr)
{
   m3dV binorm;

   cgmComputeBinormal(norm, tang, &binorm);

   matr->ElementRC(0, 0) = tang->x;   matr->ElementRC(1, 0) = binorm.x;   matr->ElementRC(2, 0) = norm->x;   matr->ElementRC(3, 0) = 0.f;
   matr->ElementRC(0, 1) = tang->y;   matr->ElementRC(1, 1) = binorm.y;   matr->ElementRC(2, 1) = norm->y;   matr->ElementRC(3, 1) = 0.f;
   matr->ElementRC(0, 2) = tang->z;   matr->ElementRC(1, 2) = binorm.z;   matr->ElementRC(2, 2) = norm->z;   matr->ElementRC(3, 2) = 0.f;
   matr->ElementRC(0, 3) = 0.f;       matr->ElementRC(1, 3) = 0.f;        matr->ElementRC(2, 3) = 0.f;       matr->ElementRC(3, 3) = 1.f;

   return;
}


/*************************************************************
* cgmTANG_SPACE::cgmTANG_SPACE ()
*
*************************************************************/
cgmTANG_SPACE::cgmTANG_SPACE (void)
{
   normalList        = NULL;
   tangentList       = NULL;
   nWeldVert         = 0;
   weldVertInd       = NULL;
   vert2FaceInd      = NULL;
   sList             = NULL;
   tList             = NULL;
   sxtList           = NULL;
   return;
}


/*************************************************************
* cgmTANG_SPACE::Calc ()
*
*************************************************************/
cgmTANG_SPACE::~cgmTANG_SPACE (void)
{
   free(normalList);
   free(tangentList);
   free(weldVertInd);
   free(vert2FaceInd);
   free(sList);
   free(tList);
   free(sxtList);
   return;
}

#define MAX_SMOOTH_GROUPS  500
#define MAX_INC_FACES 2000

/*************************************************************
* cgmTANG_SPACE::Calc ()
*
*************************************************************/
BOOL cgmTANG_SPACE::Calc (int stateCalc, float normDiscont, float tangDiscont)
{
   cgmSEARCH_VLIST  weldSearch;
   cgmWED_TS        wed;
   cgmWED_NODE     *pNode;
   cgmWED_FACE     *incFaceList[MAX_INC_FACES];
   cgmWED_FACE     *smoothGroup[MAX_SMOOTH_GROUPS];
   m3dV             perp1, perp2;
   m3dV            *vertList;
   int              i, j, k, nIncFace, nSmooth, ind2, indVert1, indVert2;
   int              stateDisc;
   float            angle1;

   ASSERT(normDiscont > 0.f && tangDiscont > 0.f);

   if (!weldVertInd) {
      // weld vertices
      if ((weldVertInd = (int*)malloc(GetNVert() * sizeof(int))) == NULL) {
         return FALSE;
      }
      if (_IsState(stateCalc, CGM_TS_CALC_WELD_VERT)) {
         if (!weldSearch.Init(GetNVert(), sizeof(m3dV), GetFaceVertList())) {
            return FALSE;
         }
         weldSearch.SetAccuracy(0.001f);
         nWeldVert = weldSearch.Weld(weldVertInd);
         weldSearch.Term();
      } else {
         nWeldVert = GetNVert();
         for (i = 0; i < nWeldVert; i ++) {
            weldVertInd[i] = i;
         }
      }
   }
   
   if (!vert2FaceInd) {
      // fill vertex to face index
      if (_IsState(stateCalc, CGM_TS_IND_VERT_2_FACE)) {
         if ((vert2FaceInd = (int*)malloc(GetNVert() * sizeof(int))) == NULL) {
            return FALSE;
         }

         for (i = 0; i < GetNFace(); i ++) {
            for (j = 0; j < 3; j ++) {
               vert2FaceInd[GetFaceVertIdx(i, j)] = 3 * i + j;
            }
         }
      }
   }

   if (!_IsState(stateCalc, CGM_TS_CALC_NORM | CGM_TS_CALC_TANG)) {
      return TRUE;
   }
   
   // create wed
   wed.SetTS(this);
   if (!wed.Init()) {
      return FALSE;
   }

   // normal list need in both cases - norm or tang calculation
   if ((normalList = (m3dV*)realloc(normalList, 3 * GetNFace() * sizeof(m3dV))) == NULL) {
      return FALSE;
   }
   stateDisc = 0;
   if (_IsState(stateCalc, CGM_TS_CHECK_DISC_TEX)) {
      stateDisc |= CGM_TS_CHECK_DISC_TEX;
   }

   if (_IsState(stateCalc, CGM_TS_CALC_NORM)) {
      // calc normals
      memset(normalList, 0, 3 * GetNFace() * sizeof(m3dV));

      for (i = 0; i < wed.GetNNode(); i ++) {
         pNode    = wed.GetNode(i);
         nIncFace = pNode->GetIncidentFaceList(incFaceList, _NElem(incFaceList));
         while ((nSmooth = GetSmoothGroup(stateDisc | CGM_TS_CHECK_DISC_NORM, normDiscont, tangDiscont, i, nIncFace, incFaceList, smoothGroup)) > 0) {
            for (j = 0; j < nSmooth; j ++) {
               // get normal
               GetFacePerpVector(smoothGroup[j]->ind, &perp1);
               indVert1 = FindVertInFace(smoothGroup[j]->ind, i);
               angle1   = GetFaceAngle(smoothGroup[j]->ind, indVert1);
               // add normal to smoothing group
               for (k = 0; k < nSmooth; k ++) {
                  indVert2 = FindVertInFace(smoothGroup[k]->ind, i);
                  ind2     = 3 * smoothGroup[k]->ind + indVert2;
                  _m3dLineCombine(&normalList[ind2], &perp1, angle1, &normalList[ind2]);
               }
            }
         }
      }
      for (i = 0; i < 3 * GetNFace(); i++) {
         //ASSERT(!m3dIsVZero(&normalList[i]));
         if (!_m3dNormalize(&normalList[i])) {
            normalList[i] = m3dVUnitY;
         }
      }
   } else {
      // fill normals list
      for (i = 0; i < GetNFace(); i ++) {
         for (j = 0; j < 3; j ++) {
            normalList[3*i+j] = *GetFaceVertNormal(i, j);
         }
      }
   }
   
   if (_IsState(stateCalc, CGM_TS_CALC_TANG)) {
      if ((tangentList = (m4dV*)realloc(tangentList, 3 * GetNFace() * sizeof(m4dV))) == NULL) {
         return FALSE;
      }
      
      if (GetFaceVertTexCoord(0, 0) != NULL) {
         // compute tangents here
         if ((sList = (m3dV*)malloc(GetNFace() * sizeof(m3dV))) == NULL) {
            return FALSE;
         }
         if ((tList = (m3dV*)malloc(GetNFace() * sizeof(m3dV))) == NULL) {
            return FALSE;
         }
         if ((sxtList = (m3dV*)malloc(GetNFace() * sizeof(m3dV))) == NULL) {
            return FALSE;
         }
         vertList = GetFaceVertList();
         for (i = 0; i < GetNFace(); i ++) {
			   m3dV     edge0, edge1, sxt;
            m3dVTX  *vtx0, *vtx1, *vtx2;

            vtx0 = GetFaceVertTexCoord(i, 0);
            vtx1 = GetFaceVertTexCoord(i, 1);
            vtx2 = GetFaceVertTexCoord(i, 2);

			   // create an edge out of x, s and t
            //cgmFACE * const face_i = GetFaceList() + i;
            int idx0 = GetFaceVertIdx(i, 0);
            int idx1 = GetFaceVertIdx(i, 1);
            int idx2 = GetFaceVertIdx(i, 2);
            edge0.x = vertList[idx1].x - vertList[idx0].x;
            edge1.x = vertList[idx2].x - vertList[idx0].x;
			   edge0.y = vtx1->s - vtx0->s;
			   edge0.z = vtx1->t - vtx0->t;
			   edge1.y = vtx2->s - vtx0->s;
			   edge1.z = vtx2->t - vtx0->t;

            m3dCrossProduct(&edge0, &edge1, &sxt);

            float a = sxt.x; // = edge0->y * edge1->z - edge0->z * edge1->y
            float b = sxt.y; // = edge0->z * edge1->x - edge0->x * edge1->z
            float c = sxt.z; // = edge0->x * edge1->y - edge0->y * edge1->x

            float ds_dx = 0.f;
            float dt_dx = 0.f;
            if (m3dFAbs(a) > M3D_EPSILON) {
               ds_dx = - b / a;
               dt_dx = - c / a;
            }

			   // create an edge out of y, s and t
            edge0.x = vertList[idx1].y - vertList[idx0].y;
            edge1.x = vertList[idx2].y - vertList[idx0].y;
            m3dCrossProduct(&edge0, &edge1, &sxt);

            a = sxt.x;
            b = sxt.y;
            c = sxt.z;

            float ds_dy = 0.f;
            float dt_dy = 0.f;
            if (m3dFAbs(a) > M3D_EPSILON) {
               ds_dy = -b / a;
               dt_dy = -c / a;
            }

			   // create an edge out of z, s and t
            edge0.x = vertList[idx1].z - vertList[idx0].z;
            edge1.x = vertList[idx2].z - vertList[idx0].z;
            m3dCrossProduct(&edge0, &edge1, &sxt);

            a = sxt.x;
            b = sxt.y;
            c = sxt.z;

            float ds_dz = 0.f;
            float dt_dz = 0.f;
            if (m3dFAbs(a) > M3D_EPSILON) {
               ds_dz = -b / a;
               dt_dz = -c / a;
            }

            // generate coordinate frame from the gradients
            sList[i].x = ds_dx;
            sList[i].y = ds_dy;
            sList[i].z = ds_dz;

            tList[i].x = dt_dx;
            tList[i].y = dt_dy;
            tList[i].z = dt_dz;

            if (!_m3dNormalize(&sList[i]) || !_m3dNormalize(&tList[i])) {
               sList[i] = m3dVUnitX;
               tList[i] = m3dVUnitY;
            }
            m3dCrossProduct(&sList[i], &tList[i], &sxtList[i]);
			   m3dNormalize(&sxtList[i]);
         }
         // calc normals
         memset(tangentList, 0, 3 * GetNFace() * sizeof(m4dV));

         for (i = 0; i < wed.GetNNode(); i ++) {
            pNode    = wed.GetNode(i);
            nIncFace = pNode->GetIncidentFaceList(incFaceList, _NElem(incFaceList));

            while ((nSmooth = GetSmoothGroup(stateDisc |CGM_TS_CHECK_DISC_TANG, normDiscont, tangDiscont, i, nIncFace, incFaceList, smoothGroup)) > 0) {
               for (j = 0; j < nSmooth; j ++) {
                  // get normal
                  indVert1 = FindVertInFace(smoothGroup[j]->ind, i);
                  perp1    = sList[smoothGroup[j]->ind];
                  angle1   = GetFaceAngle(smoothGroup[j]->ind, indVert1);
                  // add normal to smoothing group
                  for (k = 0; k < nSmooth; k ++) {
                     indVert2 = FindVertInFace(smoothGroup[k]->ind, i);
                     ind2     = 3 * smoothGroup[k]->ind + indVert2;
                     _m3dLineCombine(&tangentList[ind2], &perp1, angle1, &tangentList[ind2]);
                  }
               }
            }
         }
         for (i = 0; i < 3 * GetNFace(); i++) {
            if (m3dIsVZero(&tangentList[i])) {
               tangentList[i] = m3dVUnitX;
            }
         }
         for (i = 0; i < 3 * GetNFace(); i++) {
            ASSERT(!m3dIsVZero(&tangentList[i]));
            // orthonormalize tangent and normal
            m3dTangentComponent(&tangentList[i], &normalList[i], &tangentList[i]);
            if (!_m3dNormalize(&tangentList[i])) {
               (m3dV)tangentList[i] = m3dVUnitY;
            }

            // store -1 to w if texture coordinates are mirrored
            m3dV   binormal;
            m3dCrossProduct(&normalList[i], &tangentList[i], &binormal);

            tangentList[i].w =  1.f;
            if (m3dDotProduct(&binormal, &tList[i / 3]) < 0.f) {
               float a1 = m3dAngleVector(&binormal, &tList[i / 3]);
               if (a1 > tangDiscont) {
                  tangentList[i].w = -1.f;
               } else {
                  int aa = 0;
               }
            }
         }

         // make sure all face binormals faced same way
         for (i = 0; i < 3 * GetNFace(); i+=3) {
            if (tangentList[i + 0].w != tangentList[i + 1].w) {
               if (tangentList[i + 0].w != tangentList[i + 2].w) {
                  tangentList[i + 0].w = -tangentList[i + 0].w;
               } else {
                  tangentList[i + 1].w = -tangentList[i + 1].w;
               }
            } else {
               if (tangentList[i + 0].w != tangentList[i + 2].w) {
                  tangentList[i + 2].w = -tangentList[i + 2].w;
               }
            }
         }

         free(sList);   sList   = NULL;
         free(tList);   tList   = NULL;
         free(sxtList); sxtList = NULL;
      }  else {      // no texture coors
         for (i = 0; i < GetNFace(); i ++) {
            GetFacePerpVector(i, &perp1);
            if ((m3dFAbs(perp1.x) < M3D_EPSILON) && (m3dFAbs(perp1.z) < M3D_EPSILON))   {
               // then vz has direction of oY axis
               perp2 = m3dVUnitX;
            } else {
               perp2 = m3dVUnitY;
            }
            for (j = 0; j < 3; j ++) {
               m3dTangentComponent(&perp2, &perp1, &tangentList[3*i+j]);
               m3dNormalize(&tangentList[3*i+j]);
               tangentList[3*i+j].w = 1.f;
            }
         }
      }

      /*
      if (_IsState(stateCalc, CGM_TS_UNSHARE_TANG)) {
         // unshare tangent
         for (i = 0; i < GetNFace(); i ++) {
            for (j = 0; j < 3; j ++) {
               tangentList[3*i+j] = *GetFaceVertTangent(i, j);
            }
         }
      }
      */
   }
   wed.Term();
   return TRUE;
}
/*************************************************************
* cgmTANG_SPACE::GetSmoothGroup ()
*
*************************************************************/
int cgmTANG_SPACE::GetSmoothGroup (int stateDisc, float normDiscont, float tangDiscont, int anchorVert, int nFace, cgmWED_FACE **faceList, cgmWED_FACE **smoothGroup)
{
   cgmWED_FACE *pFace;
   int          nSmooth, nDeleted;
   int          i, j;

   pFace = NULL;
   for (i = 0; i < nFace; i ++) {
      if ((pFace = faceList[i]) != NULL) {
         break;
      }
   }
   if (pFace == NULL) {
      return 0;
   }
   nSmooth = 0;
   GetSmoothGroup_AddFace(stateDisc, normDiscont, tangDiscont, anchorVert, pFace, smoothGroup, &nSmooth);
   nDeleted = 0;
   for (i = 0; i < nSmooth; i ++) {
      for (j = 0; j < nFace; j ++) {
         if (smoothGroup[i] == faceList[j]) {
            faceList[j] = NULL;
            nDeleted ++;
         }
      }
   }
   ASSERT(nSmooth > 0 && nDeleted == nSmooth);
   return nSmooth;
}
/*************************************************************
* cgmTANG_SPACE::GetSmoothGroup ()
*
*************************************************************/
void cgmTANG_SPACE::GetSmoothGroup_AddFace (int stateDisc, float normDiscont, float tangDiscont, int anchorVert, cgmWED_FACE *pFace, cgmWED_FACE **smoothGroup, int *nSmooth)
{
   cgmWED_FACE *pFaceInc;
   cgmWED_EDGE *pEdge;
   m3dV         perp1, perp2;
   int          i, ind1, ind2;

   for (i = 0; i < *nSmooth; i ++) {
      if (smoothGroup[i] == pFace) {
         // already added
         return;
      }
   }

   STRONG_ASSERT(*nSmooth < MAX_SMOOTH_GROUPS);

   // add face
   smoothGroup[(*nSmooth) ++] = pFace;

   for (i = 0; i < pFace->nEdge; i ++) {
      pEdge = pFace->edgeList[i];
      if (pEdge->pNode1->ind != anchorVert && pEdge->pNode2->ind != anchorVert) {
         continue;
      }
      if ((pFaceInc = pFace->IncidentFace(pEdge)) == NULL) {
         continue;
      }
      if (_IsState(stateDisc, CGM_TS_CHECK_DISC_TEX)) {
         if (!IsSameTex(pFace->ind, pFaceInc->ind)) {
            continue;
         }
      }
      if (_IsState(stateDisc, CGM_TS_CHECK_DISC_NORM)) {
         GetFacePerpVector(pFace->ind, &perp1);
         GetFacePerpVector(pFaceInc->ind, &perp2);
         if (m3dAngleVector(&perp1, &perp2) > normDiscont) {
            continue;
         }
      }
      if (_IsState(stateDisc, CGM_TS_CHECK_DISC_TANG)) {
         ind1 = 3 * pFace->ind    + FindVertInFace(pFace->ind,    anchorVert);
         ind2 = 3 * pFaceInc->ind + FindVertInFace(pFaceInc->ind, anchorVert);

         if ((normalList != NULL && m3dAngleVector(&normalList[ind1], &normalList[ind2]) > normDiscont) ||
            m3dAngleVector(&sList[pFace->ind],   &sList[pFaceInc->ind])   > tangDiscont ||
            m3dAngleVector(&tList[pFace->ind],   &tList[pFaceInc->ind])   > tangDiscont ||
            m3dAngleVector(&sxtList[pFace->ind], &sxtList[pFaceInc->ind]) > tangDiscont) {
            continue;
         }
      }
      GetSmoothGroup_AddFace(stateDisc, normDiscont, tangDiscont, anchorVert, pFaceInc, smoothGroup, nSmooth);
   }
}
/*************************************************************
* cgmTANG_SPACE::GetFacePerpVector ()
*
*************************************************************/
void cgmTANG_SPACE::GetFacePerpVector (int faceNmb, m3dV *perp)
{
   m3dV  *vertList;

   vertList = GetFaceVertList();
   m3dGetPlanePerpVector(
      &vertList[GetFaceVertIdx(faceNmb, 0)],
      &vertList[GetFaceVertIdx(faceNmb, 1)],
      &vertList[GetFaceVertIdx(faceNmb, 2)],
      perp);

   if (!_m3dNormalize(perp)) {
      *perp = m3dVUnitY;
   }
   return;
}
/*************************************************************
* cgmTANG_SPACE::GetFaceAngle ()
*
*************************************************************/
float cgmTANG_SPACE::GetFaceAngle (int faceNmb, int vertNmb)
{
   m3dV  v1, v2, *vertList;
   float angle;

   vertList = GetFaceVertList();
   int idx0 = GetFaceVertIdx(faceNmb, vertNmb);
   m3dSubtractVector(&vertList[GetFaceVertIdx(faceNmb, (3+vertNmb-1) % 3)], &vertList[idx0], &v1);
   m3dSubtractVector(&vertList[GetFaceVertIdx(faceNmb, (3+vertNmb+1) % 3)], &vertList[idx0], &v2);
   angle = m3dAngleVector(&v1, &v2);

   if (angle < 1000 * M3D_EPSILON) {
      angle = 1000 * M3D_EPSILON;
   }
   return angle;
}

/*************************************************************
* cgmTANG_SPACE::GetFacePerpVector ()
*
*************************************************************/
int cgmTANG_SPACE::FindVertInFace (int faceNmb, int vertNmbWelded)
{
   if (vertNmbWelded == weldVertInd[GetFaceVertIdx(faceNmb, 0)]) {
      return 0;
   }
   if (vertNmbWelded == weldVertInd[GetFaceVertIdx(faceNmb, 1)]) {
      return 1;
   }
   if (vertNmbWelded == weldVertInd[GetFaceVertIdx(faceNmb, 2)]) {
      return 2;
   }
   ASSERT(FALSE);
   return 0;
}
//
// end of file "cgm_tang_space.cpp"
//


/*
            m3dV   dir1, dir2;
            m2dV   t1,   t2;

            m3dSubtractVector(&vertList[GetFaceVertInd(i, 1)], &vertList[GetFaceVertInd(i, 0)], &dir1);
            m3dSubtractVector(&vertList[GetFaceVertInd(i, 2)], &vertList[GetFaceVertInd(i, 0)], &dir2);

            if (GetFaceVertTexCoord(i, 0)) {
               m2dSubtractVector((m2dV*)GetFaceVertTexCoord(i, 1), (m2dV*)GetFaceVertTexCoord(i, 0), &t1);
               m2dSubtractVector((m2dV*)GetFaceVertTexCoord(i, 2), (m2dV*)GetFaceVertTexCoord(i, 0), &t2);
            } else {
               t1.x = dir1.x;
               t1.y = dir1.y;
               t2.x = dir2.x;
               t2.y = dir2.y;
            }
            m3dLineCombine(&dir1, -t2.x, &dir2, t1.x, &sList[i]);
            m3dLineCombine(&dir1, -t2.y, &dir2, t1.y, &tList[i]);
*/
/*
            float x1 = vertList[GetFaceVertInd(i, 1)].x - vertList[GetFaceVertInd(i, 0)].x;      
            float x2 = vertList[GetFaceVertInd(i, 2)].x - vertList[GetFaceVertInd(i, 0)].x;      
            float y1 = vertList[GetFaceVertInd(i, 1)].y - vertList[GetFaceVertInd(i, 0)].y;      
            float y2 = vertList[GetFaceVertInd(i, 2)].y - vertList[GetFaceVertInd(i, 0)].y;      
            float z1 = vertList[GetFaceVertInd(i, 1)].z - vertList[GetFaceVertInd(i, 0)].z;      
            float z2 = vertList[GetFaceVertInd(i, 2)].z - vertList[GetFaceVertInd(i, 0)].z;      
            float s1, s2, t1, t2;
            if (GetFaceVertTexCoord(i, 0)) {
               s1 = GetFaceVertTexCoord(i, 1)->s - GetFaceVertTexCoord(i, 0)->s;      
               s2 = GetFaceVertTexCoord(i, 2)->s - GetFaceVertTexCoord(i, 0)->s;      
               t1 = GetFaceVertTexCoord(i, 1)->t - GetFaceVertTexCoord(i, 0)->t;      
               t2 = GetFaceVertTexCoord(i, 2)->t - GetFaceVertTexCoord(i, 0)->t;      
            } else {
               s1 = t2 = 1.f;
               s2 = t1 = 0.f;
            }
            float r = (s1 * t2 - s2 * t1);      
            ASSERT(!m3dIsZero(r));

            r = 1.0F / r;      
            sList[i].x = (t2 * x1 - t1 * x2) * r;
            sList[i].y = (t2 * y1 - t1 * y2) * r;
            sList[i].z = (t2 * z1 - t1 * z2) * r;      
            tList[i].x = (s1 * x2 - s2 * x1) * r;
            tList[i].y = (s1 * y2 - s2 * y1) * r;
            tList[i].z = (s1 * z2 - s2 * z1) * r;
*/
